6.2 文件存储

因为内存存储不需要访问硬盘,所以相关操作通常都会以风驰电掣般的速度完成。但内存存储有一个不容忽视的缺点,那就是,存储在内存中的数据并不是持久化的。如果你的计算机或者程序可以永远也不关闭,又或者你的数据像缓存一样即使丢失了也无所谓,那么这个缺点对你来说是无伤大雅的;但很多时候,即使是对于缓存数据来说,我们还是希望数据可以在计算机关闭或者程序关闭之后继续存在。实现数据持久化有好几种不同的方式可选,其中最常见的莫过于将数据存储到诸如硬盘或者闪存这样的非易失存储器里面。

把数据存储到非易失存储器里面同样也有多种方法可选,而本节要介绍的是把数据存储到文件系统里面的相关技术。说得更具体一点,我们将要学习的是如何通过 Go语言以两种不同的方式将数据存储到文件里面:第一种方式需要用到通用的CSV(comma-separated value,逗号分隔值)文本格式,而第二种方法则需要用到Go语言特有的 gob 包。

CSV是一种常见的文件格式,用户可以通过这种格式向系统传递数据。当你需要用户提供大量数据,但是却因为某些原因而无法让用户把数据填入你提供的表单时,CSV格式就可以派上用场了:你只需要让用户使用电子表格程序(spreadsheet)输入所有数据,然后将这些数据导出为CSV文件,并将其上传到你的Web应用中,这样就可以在获得CSV文件之后,根据自己的需要对数据进行解码。同样地,你的Web应用也可以将用户的数据打包成CSV文件,然后通过向用户发送CSV文件来为他们提供数据。

gob是一种能够存储在文件里面的二进制格式,这种格式可以快速且高效地将内存中的数据序列化到一个或多个文件里面。二进制数据文件的用途非常多,比如,在进行数据备份以及有序关机[1]的时候,程序就可以使用二进制数据文件来快速地存储程序中的结构。正如缓存机制对应用程序来说非常有用一样,能够将数据暂时存储在文件里面,并在需要的时候读取这些数据,对于实现会话、购物车以及构建临时工作空间(workspace)也是非常有用的。

代码清单6-2展示了打开一个文件并对其进行写入的具体方法,在讨论CSV文件和gob二进制文件的过程中,类似的代码将会反复出现。

代码清单6-2 对文件进行读写

package main
import (
  "fmt"
  "io/ioutil"
  "os"
)
func main() {
  data := []byte("Hello World!\n")
    err := ioutil.WriteFile("data1", data, 0644)  ❶
  if err != nil {
    panic(err)
  }
  read1, _ := ioutil.ReadFile("data1")
  fmt.Print(string(read1))
    file1, _ := os.Create("data2")  ❷
  defer file1.Close()
  bytes, _ := file1.Write(data)
  fmt.Printf("Wrote %d bytes to file\n", bytes)
  file2, _ := os.Open("data2")
  defer file2.Close()
  read2 := make([]byte, len(data))
  bytes, _ = file2.Read(read2)
  fmt.Printf("Read %d bytes from file\n", bytes)
  fmt.Println(string(read2))
}

❶ 通过WriteFile 函数和ReadFile 函数对文件进行写入和读取

❷ 通过File 结构对文件进行写入和读取

为了减少需要展示的代码,代码清单6-2中的程序使用了空白标识符来省略各个函数可能会返回的错误。

在这个代码清单里面,程序使用了两种不同的方法来对文件进行写入和读取。第一种方法非常简单直接,它使用的是 ioutil 包中的 WriteFile 函数和 ReadFile 函数:在写入文件时,程序会将文件的名字、需要写入的数据以及一个用于设置文件权限的数字用作参数调用 WriteFile 函数;而在读取文件时,程序只需要将文件的名字用作参数,然后调用 ReadFile 函数即可。此外,无论是传递给 WriteFile 的数据,还是 ReadFile 返回的数据,都是一个由字节组成的切片。

比起前一种方法,使用 File 结构读写文件会显得更为麻烦一些,但这种做法的灵活性更高。在使用这种方法实现文件写入时,程序需要先调用 os 包的 Create 函数,并通过向该函数传入文件名来创建文件。使用 defer 关闭文件是一种值得提倡的做法,因为它杜绝了用户在使用文件之后忘记关闭文件的问题。 defer 语句可以将给定的函数调用推入到一个栈里面,保存在栈中的调用会在包含 defer 语句的函数返回之后执行。对我们的例子来说,这意味着 file1file2 将会在 main 函数执行完毕之后关闭。在拥有了 File 结构之后,程序就可以通过它的 Write 方法对文件进行写入。除了 Write 方法之外, File 结构还提供了其他几个用于写入文件的方法。

使用 File 结构读取文件的方法跟写入文件的方法类似:程序需要使用 os 包的 Open 函数打开文件,然后使用 File 结构提供的 Read 方法或者其他读取方法来读取文件中的数据。因为 File 结构提供了一些方法,它们允许用户定位并读取文件中的指定部分,所以使用 File 结构来读取文件比起单纯地调用 ReadFile 函数拥有更大的灵活性。

执行代码清单6-2所示的程序会创建 data1data2 两个文件,它们都包含文本 Hello World!”。

results matching ""

    No results matching ""